/**
 * Spring 2012 TCSS360 Deliverable 3
 * Otis Alexander
 */

package storage;

import java.io.File;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import model.Auction;
import model.Bidder;
import model.CalendarModel;
import model.Item;
import model.NPOStaffMember;
import model.User;
import model.UserAccess;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import exceptions.AuctionLimitReachedException;
import exceptions.BadDateException;
import exceptions.BadTimeException;
import exceptions.SameNpoInYearException;

public class ProgramStateHandler {

	ReadXMLFile readXml;
	WriteXMLFile writeXml;

	public ProgramStateHandler() {
		readXml = new ReadXMLFile();
		writeXml = new WriteXMLFile();

	}

	public void write(File f, CalendarModel cal,
			UserAccess ua) {
		Document doc = writeXml.getDoc();
		Element rootElement = doc.createElement("calendar");
		doc.appendChild(rootElement);

		Map<Date, List<Auction>> pastAuctions =
				cal.getAllPastAuctions();

		Map<Date, List<Auction>> currentAuctions =
				cal.getAllCurrentAuctions();

		Iterator<Entry<Date, List<Auction>>> list_it =
				currentAuctions.entrySet().iterator();

		writeCalendar(list_it, doc, rootElement);

		list_it = pastAuctions.entrySet().iterator();

		writeCalendar(list_it, doc, rootElement);

		Iterator<Entry<String, User>> user_it =
				ua.getUsers().entrySet().iterator();

		while (user_it.hasNext()) {
			User user = user_it.next().getValue();
			String usertype = user.getClass().getName();

			if (usertype.compareToIgnoreCase("Model.User") == 0) {

				Element auction_central_employee_element =
						doc.createElement("auction_central_employee");
				rootElement
						.appendChild(auction_central_employee_element);

				Attr fName =
						doc.createAttribute("first_name");
				fName.setValue(user.getFirstName());
				auction_central_employee_element
						.setAttributeNode(fName);
				Attr lName =
						doc.createAttribute("last_name");
				lName.setValue(user.getLastName());
				auction_central_employee_element
						.setAttributeNode(lName);
				Attr uName =
						doc.createAttribute("user_name");
				uName.setValue(user.getUserName());
				auction_central_employee_element
						.setAttributeNode(uName);
			} else if (usertype
					.compareToIgnoreCase("Model.NPOStaffMember") == 0) {
				Element npo_staff_member_element =
						doc.createElement("npo_staff_member");
				rootElement
						.appendChild(npo_staff_member_element);

				Attr fName =
						doc.createAttribute("first_name");
				fName.setValue(user.getFirstName());
				npo_staff_member_element
						.setAttributeNode(fName);
				Attr lName =
						doc.createAttribute("last_name");
				lName.setValue(user.getLastName());
				npo_staff_member_element
						.setAttributeNode(lName);
				Attr uName =
						doc.createAttribute("user_name");
				uName.setValue(user.getUserName());
				npo_staff_member_element
						.setAttributeNode(uName);
				Attr npoName =
						doc.createAttribute("npo_name");
				npoName.setValue(((NPOStaffMember) user)
						.getNPO());
				npo_staff_member_element
						.setAttributeNode(npoName);
			} else if (usertype
					.compareToIgnoreCase("Model.Bidder") == 0) {
				Element bidder_element =
						doc.createElement("bidder");
				rootElement.appendChild(bidder_element);

				Attr fName =
						doc.createAttribute("first_name");
				fName.setValue(user.getFirstName());
				bidder_element.setAttributeNode(fName);
				Attr lName =
						doc.createAttribute("last_name");
				lName.setValue(user.getLastName());
				bidder_element.setAttributeNode(lName);
				Attr uName =
						doc.createAttribute("user_name");
				uName.setValue(user.getUserName());
				bidder_element.setAttributeNode(uName);
				Attr cc =
						doc.createAttribute("credit_card");
				cc.setValue(((Bidder) user).getCreditCard());
				bidder_element.setAttributeNode(cc);
				Iterator<Entry<Item, BigDecimal>> bids_it =
						((Bidder) user).getBids()
								.entrySet().iterator();
				while (bids_it.hasNext()) {
					Entry<Item, BigDecimal> bids =
							bids_it.next();
					Item item = bids.getKey();
					BigDecimal itemBid = bids.getValue();
					Element item_element =
							doc.createElement("item");
					bidder_element
							.appendChild(item_element);

					Attr id =
							doc.createAttribute("item_unique_id");
					if (item != null) {
						id.setValue(item.get_unique_id());
						item_element.setAttributeNode(id);
						Attr bid =
								doc.createAttribute("item_bid");
						bid.setValue(itemBid.toString());
						item_element.setAttributeNode(bid);
					}
				}
			}
		}
		writeXml.setDoc(doc);
		writeXml.writeFile();
	}

	public void read(CalendarModel cal, UserAccess ua) {
		readXml.readFile();

		NodeList auctionList =
				readXml.getNodeList("auction");

		for (int i = 0; i < auctionList.getLength(); i++) {

			Node auctionNode = auctionList.item(i);

			if (auctionNode.getNodeType() == Node.ELEMENT_NODE) {

				Element auction = (Element) auctionNode;

				try {
					cal.addAuction(
							readXml.getAttributeValue(
									"auction_name", auction),
							readXml.getAttributeValue(
									"npo_name", auction),
							readXml.getAttributeValue(
									"npo_phone", auction),
							convertDate(readXml
									.getAttributeValue(
											"auction_date",
											auction)),
							Double.parseDouble(readXml
									.getAttributeValue(
											"start_time",
											auction)),
							Double.parseDouble(readXml
									.getAttributeValue(
											"end_time",
											auction)),
							Integer.parseInt(readXml
									.getAttributeValue(
											"num_of_items",
											auction)),
							readXml.getAttributeValue(
									"comments", auction));
				} catch (NumberFormatException e1) {
					System.out.println("num");
				} catch (BadDateException e1) {
					System.out.println("b date");
				} catch (BadTimeException e1) {
					System.out.println("b time");
				} catch (SameNpoInYearException e1) {
					System.out.println("npo");
				} catch (AuctionLimitReachedException e1) {
					System.out.println("limit");
				}

				NodeList itemList =
						auctionList.item(i).getChildNodes();

				Auction a =
						cal.getAuction(
								cal.generateAuctionName(
										readXml.getAttributeValue(
												"npo_name",
												auction),
										convertDate(readXml
												.getAttributeValue(
														"auction_date",
														auction))),
								convertDate(readXml
										.getAttributeValue(
												"auction_date",
												auction)));

				for (int j = 0; j < itemList.getLength(); j++) {

					Node node = itemList.item(j);

					if (node.getNodeType() == Node.ELEMENT_NODE) {

						Element item = (Element) node;

						try {
							a.addItem(
									readXml.getAttributeValue(
											"item_name",
											item),
									Integer.parseInt(readXml
											.getAttributeValue(
													"item_quantity",
													item)),
									new BigDecimal(
											readXml.getAttributeValue(
													"item_minbid",
													item)),
									readXml.getAttributeValue(
											"condition",
											item),
									readXml.getAttributeValue(
											"comments",
											item));
						} catch (NumberFormatException e1) {
							e1.printStackTrace();
						}
					}
				}
			}
		}

		Node auctionCentralNode =
				readXml.getNodeList(
						"auction_central_employee").item(0);

		if (auctionCentralNode.getNodeType() == Node.ELEMENT_NODE) {

			Element user = (Element) auctionCentralNode;

			ua.addUser(new User(readXml.getAttributeValue(
					"first_name", user), readXml
					.getAttributeValue("last_name", user),
					readXml.getAttributeValue("user_name",
							user)));
		}

		Node npoNode =
				readXml.getNodeList("npo_staff_member")
						.item(0);

		if (npoNode.getNodeType() == Node.ELEMENT_NODE) {

			Element bidder = (Element) npoNode;

			ua.addUser(new NPOStaffMember(
					readXml.getAttributeValue("first_name",
							bidder), readXml
							.getAttributeValue("last_name",
									bidder), readXml
							.getAttributeValue("user_name",
									bidder), readXml
							.getAttributeValue("npo_name",
									bidder)));
		}

		Node bidderNode =
				readXml.getNodeList("bidder").item(0);

		if (bidderNode.getNodeType() == Node.ELEMENT_NODE) {

			Element bidder = (Element) bidderNode;

			Bidder b =
					new Bidder(readXml.getAttributeValue(
							"first_name", bidder),
							readXml.getAttributeValue(
									"last_name", bidder),
							readXml.getAttributeValue(
									"user_name", bidder),
							readXml.getAttributeValue(
									"credit_card", bidder));
			ua.addUser(b);

			NodeList itemList = bidder.getChildNodes();

			for (int i = 0; i < itemList.getLength(); i++) {

				Node itemNode = itemList.item(i);

				if (itemNode.getNodeType() == Node.ELEMENT_NODE) {

					Element item = (Element) itemNode;
					if (item.hasAttributes()) {
						b.addBid(
								cal.getItem(readXml
										.getAttributeValue(
												"item_unique_id",
												item)),
								new BigDecimal(readXml
										.getAttributeValue(
												"item_bid",
												item)));
					}
				}
			}
		}
	}

	private void writeCalendar(
			Iterator<Entry<Date, List<Auction>>> it,
			Document doc, Element rootElement) {

		while (it.hasNext()) {
			Entry<Date, List<Auction>> get_auctions_it =
					it.next();
			Auction a;
			List<Item> l;
			for (int i = 0; i < get_auctions_it.getValue()
					.size(); i++) {
				a = get_auctions_it.getValue().get(i);
				l = a.getItems();
				Element auction_element =
						doc.createElement("auction");
				rootElement.appendChild(auction_element);

				Attr aName =
						doc.createAttribute("auction_name");
				aName.setValue(a.getName());
				auction_element.setAttributeNode(aName);
				Attr npo = doc.createAttribute("npo_name");
				npo.setValue(a.getNPO());
				auction_element.setAttributeNode(npo);
				Attr phone =
						doc.createAttribute("npo_phone");
				phone.setValue(a.getPhone());
				auction_element.setAttributeNode(phone);
				Attr date =
						doc.createAttribute("auction_date");
				Date d = a.getDate();
				Calendar c = new GregorianCalendar();
				c.setTime(d);
				date.setValue(c.getDisplayName(
						Calendar.MONTH, 2, Locale.ENGLISH)
						+ " "
						+ c.get(Calendar.DAY_OF_MONTH)
						+ ", " + c.get(Calendar.YEAR));
				auction_element.setAttributeNode(date);
				Attr start =
						doc.createAttribute("start_time");
				start.setValue(Double.toString(a
						.getStartTime()));
				auction_element.setAttributeNode(start);
				Attr end = doc.createAttribute("end_time");
				end.setValue(Double.toString(a.getEndTime()));
				auction_element.setAttributeNode(end);
				Attr items =
						doc.createAttribute("num_of_items");
				items.setValue(Integer.toString(a
						.getNumberOfAddedItems()));
				auction_element.setAttributeNode(items);
				Attr aComments =
						doc.createAttribute("comments");
				aComments.setValue(a.getComment());
				auction_element.setAttributeNode(aComments);
				for (int j = 0; j < l.size(); j++) {
					Element item_element =
							doc.createElement("item");
					auction_element
							.appendChild(item_element);
					Attr iName =
							doc.createAttribute("item_name");
					iName.setValue(l.get(j).getItemName());
					item_element.setAttributeNode(iName);
					Attr quantity =
							doc.createAttribute("item_quantity");
					quantity.setValue(Integer.toString(l
							.get(j).getQuantity()));
					item_element.setAttributeNode(quantity);
					Attr minBid =
							doc.createAttribute("item_minbid");
					minBid.setValue(l.get(j).getMinBid()
							.toString());
					item_element.setAttributeNode(minBid);
					Attr condition =
							doc.createAttribute("condition");
					condition.setValue(l.get(j)
							.getCondition());
					item_element
							.setAttributeNode(condition);
					Attr iComments =
							doc.createAttribute("comments");
					iComments.setValue(l.get(j)
							.getComment());
					item_element
							.setAttributeNode(iComments);
				}
			}
		}
	}

	private Date convertDate(String the_birthday) {
		SimpleDateFormat format =
				new SimpleDateFormat("MMMMM dd, yyyy");
		Date d = null;

		try {
			d = format.parse(the_birthday);
		} catch (ParseException e) {
			System.out.println("Bad date format");
		}

		return d;
	}

}
